#ifndef LUNIX_IO_IMPL
#define LUNIX_IO_IMPL

#define FOPEN64 1
#define __USE_LARGEFILE64 1

#include <stdio.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

namespace adapters { namespace details {

	typedef FILE * NativeHandleType;
	typedef Offset NativeOffsetType;

	template <class CharType>
	struct NativeApiTraits;
	
	template <>
	struct NativeApiTraits<char> {

		static inline bool create(NativeHandleType handle, char const * file_name) {
			if ((handle = ::fopen64(file_name, "a+b")) == NULL)
				return false;
			return true;
		}

		static inline bool open(NativeHandleType handle, char const * file_name) {
			if ((handle = ::fopen64(file_name, "rb+")) == NULL)
				return false;
			return true;
		}

	};

	template <>
	struct NativeApiTraits<wchar_t> {

		static inline bool create(NativeHandleType handle, wchar_t const * file_name) {
			//TODO impl this
			//if ((handle = fopen64(file_name, "a+b")) == NULL)
				return false;
			//return true;
		}

		static inline bool open(NativeHandleType handle, wchar_t const * file_name) {
			//TODO impl this
			//if ((handle = fopen64(file_name, "rb+")) == NULL)
				return false;
			//return true;
		}

	};

	class DeviceImpl {
	public :
		DeviceImpl() {}
		
		template <class Offset>
		static NativeOffsetType convert2NativeOffset(Offset off) {
			return off;
		}

		template <class OffsetType>
		inline static OffsetType nativeSeek(NativeHandleType handle, NativeOffsetType where) {
			NativeOffsetType pos = -1;
			if ((pos = ::fseeko64(handle, where, SEEK_SET)) < 0)
                return -1;
			return pos;
		}

		template <class CharType>
		inline static bool nativeOpenBinary(NativeHandleType handle, std::basic_string<CharType> const & file_name) {
			return NativeApiTraits<CharType>::open(handle, file_name.c_str());
		}

		template <class CharType>
		inline static bool nativeOpenDefault(NativeHandleType handle, std::basic_string<CharType> const & file_name) {
			return NativeApiTraits<CharType>::open(handle, file_name.c_str());
		}

		static void nativeClose(NativeHandleType handle) {
			::fclose(handle);
			handle = NULL;
		}

	private :
		DeviceImpl(DeviceImpl const &);
		DeviceImpl& operator=(DeviceImpl const &);
	};

	class WriteDeviceImpl : public DeviceImpl {
	public :
		WriteDeviceImpl() 
			: DeviceImpl(), m_RawHandle(NULL) 
		{}

		~WriteDeviceImpl() {
			::fclose(handle);
			handle = NULL;
		}

		inline static std::streamsize nativeWrite(NativeHandleType handle, char const * buf, std::streamsize n) {
	        std::streamsize writed = -1;
			if ((writed = ::fwrite(buf, 1, n, handle)) < 0)
				return -1;
			return writed;
		}

		inline NativeHandleType handle() {
			return m_RawHandle;
		}

	private :
		NativeHandleType m_RawHandle;
	};


	class ReadDeviceImpl : public DeviceImpl {
	public :
		ReadDeviceImpl() 
			: DeviceImpl(), m_RawHandle(NULL) 
		{}

		~ReadDeviceImpl() {
			::fclose(handle);
			handle = NULL;
		}

		inline static std::streamsize nativeRead(NativeHandleType handle, char * buf, std::streamsize n) {
			std::streamsize readed = 0;
			if ((readed = ::fread(buf, 1, n, handle)) < 0)
				return -1;
			return readed;
		}

		inline NativeHandleType handle() {
			return m_RawHandle;
		}

	private :
		NativeHandleType m_RawHandle;
	};
}
}

#endif